package com.rectcircle.wechat.api;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.rectcircle.wechat.exception.ApiRequestErrorException;
import com.rectcircle.wechat.ret.vo.AccessTokenRet;
import com.rectcircle.wechat.ret.vo.BasicReturn;
import com.rectcircle.wechat.ret.vo.ServerIpRet;
import com.rectcircle.wechat.util.Constants;
import com.rectcircle.wechat.util.HttpRequest;
import com.rectcircle.wechat.util.JsonHandler;
import com.rectcircle.wechat.util.ServletTools;
import com.rectcircle.wechat.vo.AccessToken;

/**
 * 对微信基础支持api的封装的主要包括：<br>
 * <ul>
 * <li>获取微信全局access_token
 * <li>获取微信服务器IP地址列表
 * <li>接口调用次数清零（每月10次）
 * <li>(重要)实现微信接口的代理
 * </ul>
 * 
 * @author RectCircle
 * @version 0.3<br>
 * 2016年7月17日-下午5:39:43<br>
 * Copyright (c) 2016 RectCircle-版权所有<br>
 *
 */
public class BasicApi {

	/**
	 * 获取access_token（公众号的全局唯一票据）<br>
	 * 官方每日访问限额2000次，请缓存下来或使用本工具包的缓存对象
	 * @param appId 公众号的APPID
	 * @param appSecret 公众号的秘钥 
	 * @return 一个AccessToken封装对象，包含acces_token（String）和有效期单位秒（int）
	 * @throws ApiRequestErrorException 请求出错将抛出如调用超过限定次数、参数错误等
	 */
	public static AccessToken obtainAccessToken(String appId, String appSecret) throws ApiRequestErrorException{
		String responseJson = obtainAccessTokenJson(appId, appSecret);
		AccessTokenRet retMsg = JsonHandler.parseJson(responseJson, AccessTokenRet.class);
		
		if(retMsg.isError()){
			throw new ApiRequestErrorException(retMsg.getErrDescription());
		} 
		
		return new AccessToken(retMsg.getAccess_token(),retMsg.getExpires_in());
	}
	
	/**
	 * 获取access_token（公众号的全局唯一票据）<br>
	 * 官方每日访问限额2000次，请缓存下来或使用本工具包的缓存对象
	 * @param appId 公众号的APPID
	 * @param appSecret 公众号的秘钥 
	 * @return Json字符串
	 */
	public static String obtainAccessTokenJson(String appId, String appSecret){
		String api = Constants.getApi("accessToken");
		String url = String.format(api, appId,appSecret);
		HttpRequest httpRequest = new HttpRequest.Builder(url)
				.build();
		return httpRequest.getHtmlByGet();
	}
	
	/**
	 * 获取微信服务器IP地址列表<br>
	 * 可以缓存下来，以便识别出哪些消息是微信官方推送给你的，
	 * 哪些消息可能是他人伪造的。
	 * 
	 * @param accessToken ACCESS_TOKEN调用接口凭据
	 * @return 一个IP地址的列表
	 * @throws ApiRequestErrorException 请求出错将抛出如调用超过限定次数、参数错误等
	 */
	public static List<String> obtainServerIp(String accessToken) throws ApiRequestErrorException {
		String responseJson = obtainServerIpJson(accessToken);
		ServerIpRet retMsg = JsonHandler.parseJson(responseJson, ServerIpRet.class);
		
		if(retMsg.isError()){
			throw new ApiRequestErrorException(retMsg.getErrDescription());
		}
		
		return retMsg.getIp_list();
	}

	/**
	 * 获取微信服务器IP地址列表<br>
	 * 可以缓存下来，以便识别出哪些消息是微信官方推送给你的，
	 * 哪些消息可能是他人伪造的。
	 * 
	 * @param accessToken ACCESS_TOKEN调用接口凭据
	 * @return Json字符串
	 */
	public static String obtainServerIpJson(String accessToken) throws ApiRequestErrorException {
		String api = Constants.getApi("serviceIP");
		String url = String.format(api, accessToken);
		
		HttpRequest httpRequest = new HttpRequest.Builder(url)
				.build();
		return httpRequest.getHtmlByGet();
	}

	/**
	 * 
	 * 对公众号的所有api调用（包括第三方帮其调用）次数进行清零：<br>
	 * 
	 * 谨慎使用，建议在某一接口调用次数达到上限时使用本接口进行清零
	 * <p>
	 * 
	 * @param accessToken ACCESS_TOKEN调用接口凭据
	 * @param appId 公众号的APPID
	 * @throws ApiRequestErrorException 请求出错将抛出如调用超过限定次数、参数错误等
	 */
	public static void clearQuota(String accessToken, String appId) throws ApiRequestErrorException{
		String responseJson = clearQuotaJson(accessToken, appId);
		BasicReturn retMsg = JsonHandler.parseJson(responseJson, BasicReturn.class);
		
		if(retMsg.isError()){
			throw new ApiRequestErrorException(retMsg.getErrDescription());
		}
		
	}
	
	/**
	 * 
	 * 对公众号的所有api调用（包括第三方帮其调用）次数进行清零：<br>
	 * 
	 * 谨慎使用，建议在某一接口调用次数达到上限时使用本接口进行清零
	 * <p>
	 * 
	 * @param accessToken ACCESS_TOKEN调用接口凭据
	 * @param appId 公众号的APPID
	 * @return Json字符串
	 */
	public static String clearQuotaJson(String accessToken, String appId){
		String api = Constants.getApi("clearQuota");
		String url = String.format(api, accessToken);
		
		Map<String, String> param = new HashMap<>();
		param.put("appid", appId);
		String requestJson = JsonHandler.ctreateJson(param);
		
		HttpRequest httpRequest = new HttpRequest.Builder(url)
				.postString(requestJson)
				.build();

		return httpRequest.getHtmlByPost();
	}

	/**
	 * 为了解决跨域，在此实现一个向微信api请求的方法
	 * <p>
	 * data格式：为json，仅接受字符串格式的json数据
	 * <pre>
	 * {
	 * 		url:"",//请求微信服务器的url，必填
	 * 		method:"GET|POST",	//请求方式，选填，不填时，存在data为POST方式；填写时以参数为主
	 * 		data:{
	 * 			//微信要求的数据
	 * 		}
	 * }
	 * </pre>
	 * 
	 * @param data
	 * @return 微信返回的字符串一般为json格式
	 */
	public static String proxyRequestWechat(String reqStr){
System.out.println(reqStr);
		Map<String, Object> reqMap = JsonHandler.parseJson(reqStr, Map.class);
		String url = (String) reqMap.get("url");
		String method = (String) reqMap.get("reqMap");
		Map<String, Object> data = (Map<String, Object>) reqMap.get("data");
		
		if(method==null){
			if(data==null){
				HttpRequest httpRequest = new HttpRequest.Builder(url).build();
				return httpRequest.getHtmlByGet();
			} else {
				HttpRequest httpRequest = new HttpRequest.Builder(url)
						.postString(JsonHandler.ctreateJson(data)).build();
				return httpRequest.getHtmlByPost();
			}
		} else {
			if(method.toUpperCase().equals("GET")){
				HttpRequest httpRequest = new HttpRequest.Builder(url).build();
				return httpRequest.getHtmlByGet();
			} else if(method.toUpperCase().equals("POST")){
				HttpRequest httpRequest = new HttpRequest.Builder(url)
						.postString(JsonHandler.ctreateJson(data)).build();
				return httpRequest.getHtmlByPost();
			} else {
				return null;
			}
		}
	}

	public static void proxyRequestWechat(HttpServletRequest request, HttpServletResponse response){
		try {
			response.getWriter().write(proxyRequestWechat(ServletTools.getRequestBody(request)));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
